package com.hup.utils.io.socket.netty;

import com.hup.utils.commons.exception.AppExceptionHandler;
import com.hup.utils.commons.swing.SimpleTestFrame;
import com.hup.utils.io.netty.NettyTCPServer;
import com.hup.utils.io.netty.NettyTCPServer.ServerCallback;
import com.hup.utils.io.netty.NettyTCPServer.ServerConfig;
import com.hup.utils.io.netty.annotation.DataHandlerMethod;
import com.hup.utils.io.netty.handler.DataHandler;
import com.hup.utils.io.netty.handler.HeartbeatHandler.IdleCallback;
import com.hup.utils.io.netty.handler.LoginHandler;
import com.hup.utils.io.netty.model.BaseDataPacket;
import com.hup.utils.io.netty.model.LoginResponsePacket;
import com.hup.utils.io.socket.netty.UDPBroadcastTest.BroadcastSender;
import com.hup.utils.io.socket.netty.model.MyLoginData;
import com.hup.utils.io.socket.netty.model.MyLoginRes;
import com.hup.utils.io.socket.netty.model.TestImageData;
import com.hup.utils.io.socket.netty.model.TestStringData;
import com.hup.utils.io.socket.netty.view.ClientItem;
import com.hup.utils.io.socket.netty.view.ServerTestForm;
import com.hup.utils.swing.ComponentUtil;
import com.hup.utils.swing.FrameUtil;
import com.hup.utils.swing.FrameUtil.Gravity;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.log4j.Log4j2;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 服务端测试类
 *
 * @author hugan
 * @date 2021/4/24
 */
@Log4j2
public class ServerDemo {
    private static final boolean enableBroadcast = true;

    public static void main(String[] args) {
        SimpleTestFrame.show(ServerDemo::new);
    }

    private final SimpleTestFrame frame;
    private ServerTestForm form;
    private ServerConfig serverConfig;
    private NettyTCPServer server;
    private HashMap<Channel, MyLoginData> mapClient;
    private BroadcastSender broadcastSender;

    public ServerDemo(SimpleTestFrame frame) {
        this.frame = frame;
        initView();
        initListener();
        initServer();
    }

    public void showFrame() {
        frame.setVisible(true);
    }

    private void initView() {
        frame.setTitle(ServerDemo.class.getSimpleName());
        frame.setSize(500, 300);
        frame.setAlwaysOnTop(true);
        FrameUtil.setLocation(frame, Gravity.MID_R);
        form = new ServerTestForm();
        frame.setContentPane(form.root);
        form.labImage.setVisible(false);
        onServerStart(false);
    }

    private void initListener() {
        form.btnStart.addActionListener(e -> {
            ((JComponent) e.getSource()).setEnabled(false);
            updateConfigByView();
            server.startServer();
        });
        form.btnStop.addActionListener(e -> {
            ((JComponent) e.getSource()).setEnabled(false);
            server.stopServer();
        });
        form.btnSendStr.addActionListener(e -> {
            String msg = form.txtInput.getText();
            mapClient.forEach((channel, loginData) -> channel.writeAndFlush(new TestStringData(msg)));
        });
        form.labImage.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                form.labImage.setVisible(false);
            }
        });
    }

    private void initServer() {
        mapClient = new HashMap<>();
        serverConfig = new ServerConfig(0, new ServerCallback() {
            @Override
            public void onServerStartResult(boolean success, Integer boundPort) {
                onServerStart(success);
                appendLog(boundPort + "绑定" + (success ? "成功" : "失败"));
                if (success) switchUDPBroadcast(true);
            }

            @Override
            public void onServerStopped() {
                onServerStart(false);
                switchUDPBroadcast(false);
                appendLog("服务端已关闭");
            }

            @SuppressWarnings("unchecked")
            @Override
            public void onClientConnectChanged(boolean onLine, Channel channel) {
                appendLog(channel.remoteAddress() + (onLine ? "已连接" : "已断开"));
                if (onLine) {
                    LoginHandler<MyLoginData, LoginResponsePacket> loginHandler = (LoginHandler<MyLoginData, LoginResponsePacket>) serverConfig.getLoginHandler();
                    MyLoginData loginRequestData = null;//null代表,没有认证功能
                    if (loginHandler != null) loginRequestData = loginHandler.getLoginRequestData(channel);
                    mapClient.put(channel, loginRequestData);
                } else {
                    mapClient.remove(channel);
                }
                updateClientListView();
            }

            @Override
            public void onUnknownData(ChannelHandlerContext ctx, BaseDataPacket data) {
                appendLog(data.toString());
            }
        });
        DataHandler[] dataHandlers = new DataHandler[]{
                //测试DataMethodInvokerParser
                //new PrivateDataHandler(),//private内部类
                //new DataHandler() {
                //},//匿名内部类
                //new TestRegisterMethodDataHandler(),
                new MyDataHandler(),//public内部类
        };
        for (DataHandler dataHandler : dataHandlers) {
            try {
                serverConfig.addDataHandler(dataHandler);
                log.info("添加成功:{}", dataHandler.getClass());
            } catch (Exception e) {
                log.error("拦截异常验证:{}", e.getMessage());
            }
        }
        serverConfig.setAddHeartbeat(true, new IdleCallback() {
        });
        server = new NettyTCPServer(serverConfig);
    }

    private void updateConfigByView() {
        try {
            serverConfig.port(Integer.parseInt(form.txtPort.getText()));
            serverConfig.bindRetryTime(Integer.parseInt(form.txtRetryTime.getText()));
            serverConfig.loginHandler(!form.cbLoginValid.isSelected() ? null : new LoginHandler<MyLoginData, MyLoginRes>() {
                @Override
                protected Class<MyLoginData> getLoginRequestType() {
                    return MyLoginData.class;
                }

                @Override
                protected void doLoginValid(SocketAddress address, MyLoginData data) throws LoginException {
                    log.info("data={}", data);
                    appendLog("正在校验:" + data);
                    //throw new LoginException(data.name + ":不能登录");
                    //throw new IllegalArgumentException("出现代码bug");
                }

                @Override
                protected MyLoginRes getLoginSuccessResponse(Channel channel, MyLoginData loginReq) {
                    return new MyLoginRes("服务端:" + ThreadLocalRandom.current().nextInt(10, 99));
                }
            });
        } catch (Exception e) {
            AppExceptionHandler.handle("更新配置异常:", e);
            appendLog("更新配置异常:" + e.getMessage());
        }
    }

    private void updateClientListView() {
        form.pClient.removeAll();
        form.pClient.setLayout(new GridLayout(mapClient.size(), 1));
        for (Entry<Channel, MyLoginData> entry : mapClient.entrySet()) {
            ClientItem item = new ClientItem();
            item.setClient(entry.getKey(), entry.getValue());
            form.pClient.add(item);
        }
        ComponentUtil.afterChangedChild(form.pClient);
    }

    private void appendLog(String msg) {
        log.info("{}", msg);
        ComponentUtil.appendAndScroll(form.txtContent, msg + "\n");
    }

    private void onServerStart(boolean isStart) {
        form.btnStart.setEnabled(!isStart);
        form.btnStop.setEnabled(isStart);
    }

    private void switchUDPBroadcast(boolean isOn) {
        if (!enableBroadcast) return;
        log.info("isOn={}", isOn);
        if (isOn) {
            try {
                broadcastSender = new BroadcastSender();
                broadcastSender.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            if (broadcastSender != null) {
                broadcastSender.doStop();
                broadcastSender = null;
            }
        }
    }

    private static class PrivateDataHandler implements DataHandler {
    }

    public class MyDataHandler implements DataHandler {
        @DataHandlerMethod
        public void handleString(TestStringData data, Channel channel) {
            log.info("data={}, channel={}", data, channel);
            appendLog(channel.remoteAddress().toString() + ":" + data.msg);
        }

        @DataHandlerMethod
        public void handleImage(TestImageData data) {
            log.info("data={}", data);
            if (data.getImage() != null) {
                form.labImage.setVisible(true);
                form.labImage.setIcon(data.getImage());
            }
        }
    }

}
